/*****************************************
 * Copyright (C) 2022 * Ltd. All rights reserved.
 *
 * File name   : HandlerLooper.hpp
 * Author      : longbin
 * Created date: 2022-03-10 16:44:18
 * Description :
 *
 *******************************************/

// usage:
// 1. create an instance of HLHandlerThread. then start the handler thread.
// auto handlerThread = std::make_shared<HLHandlerThread>("HandlerThread");
// handlerThread->start();
// assert(handlerThread->getLooper());
//
// 2. create an instance derived from HLHandler with the handler thread looper.
// auto handler = std::make_shared<MyHandler>(handlerThread->getLooper());
//    or create an instance from HLHandler with the handler thread looper and a function.
// auto handler =
//     std::make_shared<HLHandler>(handlerThread->getLooper(),
//         [](const HLMessageRefPtr & m) {
//             handleMessage(m);
//             return true;
//         });
//
// 3. use the handler to send message or post method.
// handler->post([](const HLMessageRefPtr &) {
//         LOGI("post callback test.");
//         return true;
//     });
//
// handler->postDelayed([](const HLMessageRefPtr &) {
//         LOGI("postDelayed callback test.");
//         return true;
//     },
//     1000);
//
// handler->sendMessage(3, 4, 5);
// handler->sendMessage(3, 4, 5, make_any_obj<int>(8));
// handler->sendMessage(4, 5, 6, make_any_obj<std::string>(s4));
// handler->sendMessage(4, 5, 6, make_any_obj<std::string>("hello string"));
//
// std::vector<int> v5 {1, 2, 3, 4, 5};
// handler->sendMessage(5, 6, 7, make_any_obj<std::vector<int>>(v5));
//
// std::vector<int> v52(10);
// std::iota(v52.begin(), v52.end(), 100);
// handler->sendMessage(5, 6, 7, make_any_obj<std::vector<int>>(v52));
// handler->sendMessage(5, 6, 7, make_any_obj<std::vector<int>>(1, 2, 3, 4, 5));
//
// using ST = struct {int id; std::string info};
// ST st{1, "hello ST 1"};
// handler->sendMessage(6, 7, 8, make_any_obj<ST>(st));
// handler->sendMessage(6, 7, 8, make_any_obj<ST>(2, "hello ST 2"));
//
// 4. call quit() method to quit its looper, and stop() to quit its looper and terminate its thread.
// handlerThread->quit();
// handlerThread->stop();
//

#ifndef __HANDLER_LOOPER_H__
#define __HANDLER_LOOPER_H__

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <deque>
#include <memory>
#include <numeric>
#include <typeindex>
#include <thread>
#include <chrono>
#include <future>
#include <algorithm>
#include <cstdarg>
#include <functional>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <cassert>

namespace handlerlooper
{
class HLMessage;
class HLMessageQueue;
class HLLooper;
class HLThread;
class HLHandlerThread;

// HLHandlerThread is derived from HLThread and also contains a HLLooper.
// HLLooper will loop to obtain message and call HLHandler->dispatchMessage();
// HLHandler has sendMessage and handleMessage method. Its handleMessage method is virtual, should
// be override if the message doesnot figure the handler.

using HLMessageRefPtr = std::shared_ptr<HLMessage>;
using HLMessageRefPtrList = std::deque<HLMessageRefPtr>;
using HLMessageQueueRefPtr = std::shared_ptr<HLMessageQueue>;
using HLLooperRefPtr = std::shared_ptr<HLLooper>;

using HLCallback = std::function<bool(const HLMessageRefPtr &)>;

class HLIHandler
{
public:
    virtual void dispatchMessage(const HLMessageRefPtr &) = 0;
};
using HLHandlerPtr = HLIHandler *;

#ifndef HAS_RTTI
#    define HAS_RTTI 0
#endif

#if HAS_RTTI
class HLAny
{
private:
    class Base;
    using BasePtr = std::unique_ptr<Base>;

    struct Base {
        virtual ~Base()
        {}

        virtual BasePtr clone() const = 0;
    };

    template <typename T>
    struct Derived : Base {
        T mValue{};

        template <typename U>
        Derived(U &&value) : mValue(std::forward<U>(value))
        {}

        BasePtr clone() const
        {
            return BasePtr(new Derived<T>(mValue));
        }
    };

private:
    BasePtr mPtr;
    std::type_index mTypeIndex;

    BasePtr clone() const
    {
        return mPtr ? mPtr->clone() : nullptr;
    }

public:
    HLAny(void) : mTypeIndex(std::type_index(typeid(void)))
    {}

    HLAny(const HLAny &another) : mPtr(another.clone()), mTypeIndex(another.mTypeIndex)
    {}

    HLAny(HLAny &&another) : mPtr(std::move(another.mPtr)), mTypeIndex(another.mTypeIndex)
    {}

    // main constructor
    template <typename U, class = typename std::enable_if<
                              !std::is_same<typename std::decay<U>::type, HLAny>::value, U>::type>
    HLAny(U &&value)
        : mPtr(new Derived<typename std::decay<U>::type>(std::forward<U>(value))),
          mTypeIndex(std::type_index(typeid(typename std::decay<U>::type)))
    {}

    HLAny &operator=(const HLAny &another)
    {
        if (another.mPtr != mPtr) {
            mPtr = another.clone();
            mTypeIndex = another.mTypeIndex;
        }
        return *this;
    }

    void reset(void)
    {
        mPtr.reset();
    }

    bool has_value(void) const
    {
        return bool(mPtr);
    }

    template <typename U>
    inline bool is() const
    {
        return mTypeIndex == std::type_index(typeid(U));
    }

    template <class U>
    U &any_cast() const
    {
        if (!is<U>()) {
            // LOGI("cannot cast %s to %s", typeid(U).name(), mTypeIndex.name());
            // std::cout << "cannot cast " << typeid(U).name() << " to " << mTypeIndex.name() <<
            // std::endl;
            throw std::bad_cast();
        }

        auto derived = dynamic_cast<Derived<U> *>(mPtr.get());
        return derived->mValue;
    }

}; // class HLAny

using HLObject = HLAny;

#    ifndef __MAKE_ANY_OBJ_TEMPLATE_FUNCTION__
#        define __MAKE_ANY_OBJ_TEMPLATE_FUNCTION__

template <typename T, typename... Args>
HLObject make_any_obj(Args &&... args)
{
    return HLObject(T{std::forward<Args>(args)...});
}

template <typename T>
T cast_any_obj(const HLObject &any)
{
    return any.is<T>() ? any.any_cast<T>() : T{};
}
#    endif // __MAKE_ANY_OBJ_TEMPLATE_FUNCTION__

#else // HAS_RTTI

using HLAny = void *;
using HLObject = std::shared_ptr<HLAny>;

#    ifndef __MAKE_ANY_OBJ_TEMPLATE_FUNCTION__
#        define __MAKE_ANY_OBJ_TEMPLATE_FUNCTION__

template <typename T, typename... Args>
HLObject make_any_obj(Args &&... args)
{
    return HLObject((HLAny *)new T{std::forward<Args>(args)...},
                    [](const HLAny *p) { delete (T *)p; });
}

template <typename T>
T cast_any_obj(const HLObject &sp)
{
    return sp ? (*reinterpret_cast<T *>(sp.get())) : T{};
}
#    endif // __MAKE_ANY_OBJ_TEMPLATE_FUNCTION__

#endif // HAS_RTTI

class HLMessage
{
private:
    static constexpr size_t kMessagePoolMaxSize = 300U;

public:
    int what = 0;
    int arg1 = 0;
    int arg2 = 0;
    HLObject obj = nullptr;
    long long when = 0;
    HLHandlerPtr handler = nullptr;
    HLCallback callback = nullptr;

private:
    inline void clear(void)
    {
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = nullptr;
        when = 0;
        handler = nullptr;
        callback = nullptr;
    }

    static inline HLMessageRefPtrList &messageRecyclingPool()
    {
        static HLMessageRefPtrList sMessageRecyclingPool;
        return sMessageRecyclingPool;
    }

    static inline std::recursive_mutex &messagePoolMuxLock()
    {
        static std::recursive_mutex sMessagePoolMuxLock;
        return sMessagePoolMuxLock;
    }

public:
    HLMessage()
    {}

    explicit HLMessage(const int &what, const int &arg1 = 0, const int &arg2 = 0,
                       const HLObject &obj = nullptr)
        : what(what), arg1(arg1), arg2(arg2), obj(obj), when(0), handler(nullptr), callback(nullptr)
    {}

    HLMessage(const HLMessage &another)
        : what(another.what), arg1(another.arg1), arg2(another.arg2), obj(another.obj),
          when(another.when), handler(another.handler), callback(another.callback)
    {}

    HLMessage &operator=(const HLMessage &another)
    {
        this->what = another.what;
        this->arg1 = another.arg1;
        this->arg2 = another.arg2;
        this->obj = another.obj;
        this->when = another.when;
        this->handler = another.handler;
        this->callback = another.callback;
        return *this;
    }

    HLMessage(HLMessage &&another)
        : what(another.what), arg1(another.arg1), arg2(another.arg2), obj(std::move(another.obj)),
          when(std::move(another.when)), handler(std::move(another.handler)),
          callback(std::move(another.callback))
    {}

    HLMessage &operator=(HLMessage &&another)
    {
        this->what = another.what;
        this->arg1 = another.arg1;
        this->arg2 = another.arg2;
        this->obj = std::move(another.obj);
        this->when = std::move(another.when);
        this->handler = std::move(another.handler);
        this->callback = std::move(another.callback);
        return *this;
    }

    virtual ~HLMessage()
    {}

    static inline HLMessageRefPtr obtain(void)
    {
        {
            std::lock_guard<std::recursive_mutex> lg(messagePoolMuxLock());

            if (messageRecyclingPool().size() > 0) {
                HLMessageRefPtr msg = std::move(messageRecyclingPool().front());
                messageRecyclingPool().pop_front();
                msg->clear();
                return msg;
            }
            // unlock here to minimize the lock scope.
        }

        return std::make_shared<HLMessage>();
    }

    static inline HLMessageRefPtr obtain(const int &what)
    {
        HLMessageRefPtr msg = obtain();

        msg->what = what;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const int &what, const int &arg1)
    {
        HLMessageRefPtr msg = obtain();

        msg->what = what;
        msg->arg1 = arg1;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const int &what, const int &arg1, const int &arg2)
    {
        HLMessageRefPtr msg = obtain();

        msg->what = what;
        msg->arg1 = arg1;
        msg->arg2 = arg2;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const HLHandlerPtr &handler)
    {
        HLMessageRefPtr msg = obtain();

        msg->handler = handler;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const HLHandlerPtr &handler, const int &what)
    {
        HLMessageRefPtr msg = obtain();

        msg->handler = handler;
        msg->what = what;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const HLHandlerPtr &handler, const int &what,
                                         const int &arg1)
    {
        HLMessageRefPtr msg = obtain();

        msg->handler = handler;
        msg->what = what;
        msg->arg1 = arg1;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const HLHandlerPtr &handler, const int &what,
                                         const int &arg1, const int &arg2)
    {
        HLMessageRefPtr msg = obtain();

        msg->handler = handler;
        msg->what = what;
        msg->arg1 = arg1;
        msg->arg2 = arg2;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const HLHandlerPtr &handler, const int &what,
                                         const int &arg1, const int &arg2, const HLObject &obj)
    {
        HLMessageRefPtr msg = obtain();

        msg->handler = handler;
        msg->what = what;
        msg->arg1 = arg1;
        msg->arg2 = arg2;
        msg->obj = obj;
        return msg;
    }

    static inline HLMessageRefPtr obtain(const HLHandlerPtr &handler, const HLCallback &callback)
    {
        HLMessageRefPtr msg = obtain();

        msg->handler = handler;
        msg->callback = callback;
        return msg;
    }

    /// @brief recycle message to message pool
    /// @param message
    static void recycle(HLMessageRefPtr &&message)
    {
        std::lock_guard<std::recursive_mutex> lg(messagePoolMuxLock());

        if (messageRecyclingPool().size() < kMessagePoolMaxSize) {
            HLMessageRefPtr msg = std::move(message);
            if (msg && msg.unique()) {
                messageRecyclingPool().push_front(std::move(msg));
            }
        }
        return;
    }
}; // class HLMessage

class HLMessageQueue
{
private:
    HLMessageRefPtrList mMessages{};
    mutable std::mutex mMuxLock;
    std::condition_variable mCond;

private:
    HLMessageQueue(const HLMessageQueue &) = delete;
    HLMessageQueue &operator=(const HLMessageQueue &) = delete;

    static inline long long steadyTimeMillis(void)
    {
        auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now());
        std::chrono::milliseconds span =
            std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
        return span.count();
    }

    inline void waitForItems(void)
    {
        std::unique_lock<std::mutex> ul(mMuxLock);
        mCond.wait(ul, [this]() { return !mMessages.empty(); });

        if (0 == mMessages.front()->when) {
            return;
        }

        long long toNextItemTimeUpMillis = 0;
        while (!mMessages.empty()
               && (toNextItemTimeUpMillis = mMessages.front()->when - steadyTimeMillis()) > 0) {
            // next message is not ready, set a timeout to wake up when it is ready.
            mCond.wait_for(ul, std::chrono::milliseconds(toNextItemTimeUpMillis));
        }
    }

public:
    HLMessageQueue()
    {}

    virtual ~HLMessageQueue()
    {}

    inline HLMessageRefPtr next(void)
    {
        while (true) {
            waitForItems();
            do {
                std::lock_guard<std::mutex> lg(mMuxLock); // minimise the lock scope.
                if (mMessages.empty()) {
                    continue;
                }
                if (steadyTimeMillis() >= mMessages.front()->when) { // time is up.
                    HLMessageRefPtr msg = std::move(mMessages.front());
                    mMessages.pop_front();
                    return msg;
                }
            } while (0);
        }
    }

    bool enqueueMessage(HLMessageRefPtr &&message, const long long &when)
    {
        do {
            std::lock_guard<std::mutex> lg(mMuxLock);
            HLMessageRefPtr msg = std::move(message);
            msg->when = when;
            mMessages.insert(std::upper_bound(mMessages.begin(), mMessages.end(), when,
                                              [](const long long &when, const HLMessageRefPtr &m) {
                                                  return (when < m->when);
                                              }),
                             std::move(msg));
        } while (0);

        mCond.notify_one();
        std::this_thread::yield();
        return true;
    }

    void removeMessages(const HLHandlerPtr &handler, const int &what)
    {
        std::lock_guard<std::mutex> lg(mMuxLock);
        mMessages.erase(std::remove_if(mMessages.begin(), mMessages.end(),
                                       [&what, &handler](const HLMessageRefPtr &m) {
                                           return ((what == m->what) && (handler == m->handler));
                                       }),
                        mMessages.end());
    }

    bool hasMessages(const HLHandlerPtr &handler, const int &what) const
    {
        std::lock_guard<std::mutex> lg(mMuxLock);
        return mMessages.end()
               != std::find_if(mMessages.begin(), mMessages.end(),
                               [&what, &handler](const HLMessageRefPtr &m) {
                                   return ((what == m->what) && (handler == m->handler));
                               });
    }
}; // class HLMessageQueue

class HLLooper
{
public:
    HLMessageQueueRefPtr mMessageQueue = nullptr;

private:
    HLLooper(const HLLooper &) = delete;
    HLLooper &operator=(const HLLooper &) = delete;

    static inline HLLooperRefPtr &threadLocalLooper(void)
    {
        static thread_local HLLooperRefPtr sLooper = nullptr;
        return sLooper;
    }

public:
    HLLooper() : mMessageQueue(std::make_shared<HLMessageQueue>())
    {}

    ~HLLooper()
    {}

    /// @brief prepare looper
    /// @param  void
    static void prepare(void)
    {
        // the looper should be created by its running thread which will hold the loop.
        // the looper is each thread's looper, no con-current race.
        if (!threadLocalLooper()) {
            threadLocalLooper() = std::make_shared<HLLooper>();
        }
    }

    /// @brief get current thread looper
    /// @param  void
    /// @return looper of current thread
    static HLLooperRefPtr myLooper(void)
    {
        return threadLocalLooper();
    }

    /// @brief run loop for ontaining and dispatching message
    /// @param  void
    static void loop(void)
    {
        HLLooperRefPtr me = myLooper();
        assert(me); // looper should be prepared before loop.

        HLMessageQueueRefPtr queue = me->mMessageQueue;
        assert(queue);

        while (true) {
            HLMessageRefPtr message = queue->next();
            if (message) {
                if (nullptr == message->handler) {
                    // No handler is a magic identifier for the quit message.
                    break;
                }

                // dispatch message by handler.
                // message->handler->dispatchMessage(message);
                ((HLHandlerPtr)message->handler)->dispatchMessage(message);
            }

            HLMessage::recycle(std::move(message));
        }
        return;
    }

    /// @brief request to quit loop by sending a magic message
    /// @param  void
    /// @return ok ? true : false
    bool quit(void)
    {
        // message without handler is a magic identifier for the quit message.
        (void)mMessageQueue->enqueueMessage(HLMessage::obtain(), 0);
        // insert message with when equaled to -1 to message queue head and quit loop.
        return mMessageQueue->enqueueMessage(HLMessage::obtain(), -1);
    }
}; // class HLLooper

class HLHandler : public HLIHandler
{
private:
    HLLooperRefPtr mLooper;
    HLMessageQueueRefPtr mMessageQueue;
    HLCallback mCallback;

private:
    HLHandler(const HLHandler &) = delete;
    HLHandler &operator=(const HLHandler &) = delete;

public:
    HLHandler() : mLooper(HLLooper::myLooper()), mCallback(nullptr)
    {
        // should prepare/loop the looper before creating this instance.
        assert(mLooper);
        mMessageQueue = mLooper->mMessageQueue;
    }

    explicit HLHandler(const HLCallback &callback)
        : mLooper(HLLooper::myLooper()), mCallback(callback)
    {
        // should prepare/loop the looper before creating this instance.
        assert(mLooper);
        mMessageQueue = mLooper->mMessageQueue;
    }

    explicit HLHandler(const HLLooperRefPtr &looper) : mLooper(looper), mCallback(nullptr)
    {
        // should prepare/loop the looper before creating this instance.
        assert(mLooper);
        mMessageQueue = mLooper->mMessageQueue;
    }

    HLHandler(const HLLooperRefPtr &looper, const HLCallback &callback)
        : mLooper(looper), mCallback(callback)
    {
        // should prepare/loop the looper before creating this instance.
        assert(mLooper);
        mMessageQueue = mLooper->mMessageQueue;
    }

    virtual ~HLHandler()
    {}

    HLLooperRefPtr getLooper(void) const
    {
        return mLooper;
    }

    static inline long long steadyTimeMillis(void)
    {
        // static struct timespec ts {};
        // (void)clock_gettime(CLOCK_MONOTONIC, &ts);
        // return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
        auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now());
        std::chrono::milliseconds span =
            std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
        return span.count();
    }

    virtual void handleMessage(const HLMessageRefPtr &)
    {
        assert(0);
        // the derived class if exists, should override this method.
    }

    void dispatchMessage(const HLMessageRefPtr &message) final
    {
        // handle message by its callback function first.
        if (message->callback) {
            (void)message->callback(message);
            return;
        }

        // handle message by handler's callback function if exists.
        if (mCallback) {
            if (mCallback(message)) {
                return;
            }
        }

        // handle message by its derived class's handleMessage method finally.
        (void)handleMessage(message);
    }

    inline HLMessageRefPtr obtainMessage(void)
    {
        return HLMessage::obtain(this);
    }

    inline HLMessageRefPtr obtainMessage(const int &what)
    {
        return HLMessage::obtain(this, what);
    }

    inline HLMessageRefPtr obtainMessage(const int &what, const int &arg1)
    {
        return HLMessage::obtain(this, what, arg1);
    }

    inline HLMessageRefPtr obtainMessage(const int &what, const int &arg1, const int &arg2)
    {
        return HLMessage::obtain(this, what, arg1, arg2);
    }

    inline HLMessageRefPtr obtainMessage(const int &what, const int &arg1, const int &arg2,
                                         const HLObject &obj)
    {
        return HLMessage::obtain(this, what, arg1, arg2, obj);
    }

    inline HLMessageRefPtr obtainMessage(const HLCallback &callback)
    {
        return HLMessage::obtain(this, callback);
    }

    bool postAtTime(const HLCallback &callback, const long long &uptimeMillis)
    {
        return mMessageQueue->enqueueMessage(obtainMessage(callback), uptimeMillis);
    }

    bool postDelayed(const HLCallback &callback, const long long &delayMillis = 0)
    {
        return mMessageQueue->enqueueMessage(
            obtainMessage(callback), steadyTimeMillis() + ((delayMillis > 0) ? delayMillis : 0));
    }

    bool post(const HLCallback &callback)
    {
        return postAtTime(callback, 0);
    }

    bool sendMessageAtTime(HLMessageRefPtr &&message, const long long &uptimeMillis)
    {
        return mMessageQueue->enqueueMessage(std::move(message), uptimeMillis);
    }

    bool sendMessageDelayed(HLMessageRefPtr &&message, const long long &delayMillis)
    {
        return mMessageQueue->enqueueMessage(
            std::move(message), steadyTimeMillis() + ((delayMillis > 0) ? delayMillis : 0));
    }

    bool sendMessage(HLMessageRefPtr &&message)
    {
        return mMessageQueue->enqueueMessage(std::move(message), 0);
    }

    bool sendMessage(const int &what)
    {
        return sendMessage(obtainMessage(what));
    }

    bool sendMessage(const int &what, const int &arg1)
    {
        return sendMessage(obtainMessage(what, arg1));
    }

    bool sendMessage(const int &what, const int &arg1, const int &arg2)
    {
        return sendMessage(obtainMessage(what, arg1, arg2));
    }

    bool sendMessage(const int &what, const int &arg1, const int &arg2, const HLObject &obj)
    {
        return sendMessage(obtainMessage(what, arg1, arg2, obj));
    }

    bool sendMessageDelayed(const int &what, const long long &delayMillis)
    {
        return sendMessageDelayed(obtainMessage(what), delayMillis);
    }

    bool sendMessageDelayed(const int &what, const int &arg1, const long long &delayMillis)
    {
        return sendMessageDelayed(obtainMessage(what, arg1), delayMillis);
    }

    bool sendMessageDelayed(const int &what, const int &arg1, const int &arg2,
                            const long long delayMillis)
    {
        return sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis);
    }

    bool sendMessageDelayed(const int &what, const int &arg1, const int &arg2, const HLObject &obj,
                            const long long &delayMillis)
    {
        return sendMessageDelayed(obtainMessage(what, arg1, arg2, obj), delayMillis);
    }

    void removeMessages(const int &what)
    {
        mMessageQueue->removeMessages(this, what);
    }

    bool hasMessages(const int &what)
    {
        return mMessageQueue->hasMessages(this, what);
    }
}; // class HLHandler

class HLThread
{
private:
    std::thread mThread;
    std::atomic<bool> mIsAlive{false};
    std::string mName;
    int mPriority = 0;

    std::promise<bool> mThreadStartPromise;
    std::future<bool> mThreadStartFuture = mThreadStartPromise.get_future();

private:
    HLThread(const HLThread &) = delete;
    HLThread &operator=(const HLThread &) = delete;

    void waitForStarted(void)
    {
        try {
            (void)mThreadStartFuture.get();
        } catch (std::future_error &err) {
            (void)err;
            // LOGI("future_error: %s", err.what());
        } catch (std::logic_error &err) {
            (void)err;
            // LOGI("logic_error: %s", err.what());
        } catch (std::exception &err) {
            (void)err;
            // LOGI("logic_error: %s", err.what());
        }
    }

    void notifyStarted(void)
    {
        mThreadStartPromise.set_value(true);
    }

    void entry(void)
    {
        do {
            if (mPriority) {
                setPriority(mPriority);
            }

            if (!mName.empty()) {
                (void)setName(mName);
            }
        } while (0);

        mIsAlive.store(true);

        notifyStarted();
        runLoop();
    }

public:
    HLThread()
    {
        mIsAlive.store(false);
    }

    explicit HLThread(const std::string &name) : mName(name)
    {
        mIsAlive.store(false);
    }

    explicit HLThread(const std::string &name, const int &priority)
        : mName(name), mPriority(priority)
    {
        mIsAlive.store(false);
    }

    virtual ~HLThread()
    {
        mIsAlive.store(false);
        waitForThreadExit();
    }

    bool start(void)
    {
        if (mIsAlive.load()) {
            return false;
        }

        mThread = std::thread(std::bind(&HLThread::entry, this));

        waitForStarted();
        return true;
    }

    virtual bool stop(void)
    {
        mIsAlive.store(false);
        // can not join thread here if run is a loop.
        return true;
    }

    inline bool shouldExit(void) const
    {
        return !mIsAlive.load();
    }

    inline bool isRunning(void) const
    {
        return mIsAlive.load();
    }

    inline void waitForThreadExit(void)
    {
        if (mThread.joinable()) {
            mThread.join();
        }
    }

    inline bool setPriority(const int &prio)
    {
        struct sched_param param {};
        int policy = 0;
        int priority = prio;

        if (prio < 0) {
            priority = 0;
        } else if (prio > 10) {
            priority = 10;
        } else {
            // do nothing
        }
        mPriority = priority;

        // pthread_t handle = pthread_self();
        pthread_t handle = mThread.native_handle();
        if (!handle) {
            return false;
        }
        if (0 != pthread_getschedparam(handle, &policy, &param)) {
            return false;
        }

        policy = (priority == 0) ? SCHED_OTHER : SCHED_RR;
        const int minPriority = sched_get_priority_min(policy);
        const int maxPriority = sched_get_priority_max(policy);

        param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
        return (0 == pthread_setschedparam(reinterpret_cast<pthread_t>(handle), policy, &param));
    }

    inline bool setName(const std::string &name)
    {
        mName = name;
        // pthread_t handle = pthread_self();
        pthread_t handle = mThread.native_handle();
        if (!handle) {
            return false;
        }
        return (0 == pthread_setname_np(handle, name.c_str()));
    }

    inline const std::string &name(void) const
    {
        return mName;
    }

    virtual void runLoop(void)
    {
        // the derived class should override this method.
    }
}; // class HLThread

class HLHandlerThread : public HLThread
{
private:
    HLLooperRefPtr mLooper = nullptr;
    std::atomic<bool> mIsLooperPrepared{false};
    std::condition_variable mLooperPreparedCond;
    mutable std::mutex mMuxLock;

private:
    virtual void runLoop(void) override
    {
        // std::thread::id mThreadId = std::this_thread::get_id();
        // the looper should be created by its running thread which will hold the loop.
        HLLooper::prepare();
        mLooper = HLLooper::myLooper();
        onLooperPrepared();

        HLLooper::loop();
    }

    void onLooperPrepared(void)
    {
        std::lock_guard<std::mutex> lg(mMuxLock);
        mIsLooperPrepared.store(true);
        mLooperPreparedCond.notify_one();
    }

    HLLooperRefPtr waitForLooper(const long long &timeoutMillis = -1)
    {
        std::unique_lock<std::mutex> ul(mMuxLock);
        if (timeoutMillis > 0) {
            if (!mIsLooperPrepared.load() && isRunning()) {
                mLooperPreparedCond.wait_for(ul, std::chrono::milliseconds(timeoutMillis));
            }
        } else if (timeoutMillis == 0) {
            return mLooper;
        } else {
            mLooperPreparedCond.wait(
                ul, [this]() { return (mIsLooperPrepared.load() || !isRunning()); });
        }
        return mLooper; // nullptr or valid looper instance ptr.
    }

public:
    HLHandlerThread()
    {}

    explicit HLHandlerThread(const std::string &name) : HLThread(name)
    {}

    explicit HLHandlerThread(const std::string &name, const int &priority)
        : HLThread(name, priority)
    {}

    virtual ~HLHandlerThread()
    {
        (void)stop();
    }

    /// @brief quit looper and stop thread.
    /// @param  void
    /// @return ok ? true : false
    bool stop(void) override
    {
        (void)HLThread::stop();
        (void)quit();
        waitForThreadExit();
        return true;
    }

    /// @brief quit looper
    /// @param  void
    /// @return ok ? true : false
    bool quit(void)
    {
        return mLooper ? mLooper->quit() : false;
    }

    /// @brief get looper
    /// @param timeoutMillis timeout milliseconds
    /// @return ok ? HLLooperRefPtr : nullptr
    HLLooperRefPtr getLooper(const long long &timeoutMillis = -1)
    {
        return (isRunning() && mLooper) ? mLooper : waitForLooper(timeoutMillis);
    }
}; // class HLHandlerThread

};     // namespace handlerlooper
#endif //__HANDLER_LOOPER_H__
